﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Mind.Data
{
    public class WrapperGenerator
    {
        private const string BASE_TEMPLATE = @"using System;
using Mind.Data;

namespace [Namespace] 
{
	#region Generated Schema
	#endregion 

	public partial class [NormalTableName] : Wrapper<[NormalTableName], [NormalTableName]Schema>
	{
		#region Generated Properties
		#endregion
	}
    
}";

        private const string SCHEMA_TEMPLATE = @"
	public class [NormalTableName]Schema : Schema<[NormalTableName]Schema>
	{
		private const string _TableName = ""[TableName]"";
        
[ColumnNameConstants]

		protected override void GenerateColumns()
		{
[ColumnsConstruction]            
		}

		protected override void GenerateLockups()
		{
[LockupsConstruction]
		}

		protected override string GetTableName()
		{
			return _TableName;
		}
	}
";

        public static string Generate(DataSourceSchema.Table tableSchema, string namespaceValue)
        {
            return Generate(tableSchema, namespaceValue, BASE_TEMPLATE);
        }

        public static string Generate(DataSourceSchema.Table tableSchema, string namespaceValue, string baseTemplate)
        {
            StringBuilder result = new StringBuilder();
            baseTemplate = baseTemplate.Replace("[Namespace]", namespaceValue);
            baseTemplate = baseTemplate.Replace("[TableName]", tableSchema.Name);
            baseTemplate = baseTemplate.Replace("[NormalTableName]", tableSchema.Name);

            Regex schemaRegion = new Regex(@"\#region\ Generated\ Schema(?<Group>(.|\n)*?)\#endregion");

            Match schemaMatch = schemaRegion.Match(baseTemplate);

            if (schemaMatch.Success)
            {
                Group schemaGroup = schemaMatch.Groups["Group"];

                result.AppendLine(baseTemplate.Substring(0, schemaGroup.Index));
                result.AppendLine(GenerateSchemaClass(tableSchema));
                result.Append("\t");
                result.Append(baseTemplate.Substring(schemaGroup.Index + schemaGroup.Length));
            }

            baseTemplate = result.ToString();
            result.Length = 0;

            Regex classRegion = new Regex(@"\#region\ Generated\ Properties(?<Group>(.|\n)*?)\#endregion");

            Match classMatch = classRegion.Match(baseTemplate);

            if (classMatch.Success)
            {
                Group classGroup = classMatch.Groups["Group"];


                result.AppendLine(baseTemplate.Substring(0, classGroup.Index));
                result.AppendLine(GenerateWrapperClass(tableSchema));
                result.Append("\t\t");
                result.Append(baseTemplate.Substring(classGroup.Index + classGroup.Length));
            }


            return result.ToString();
        }

        private static string GenerateLockupName(DataSourceSchema.Column column)
        {
            string result = column.PrimaryKey ? column.ForeyingColumn.Name : column.Name;
            if (result.StartsWith("Id", StringComparison.InvariantCultureIgnoreCase))
            {
                result = result.Remove(0, 2);
            }
            else
                if (result.EndsWith("Id", StringComparison.InvariantCultureIgnoreCase))
                {
                    result = result.Remove(result.Length - 2);
                }
                else
                    result = String.Format("{0}Value", result);

            return result;
        }

        private static string GenerateWrapperClass(DataSourceSchema.Table tableSchema)
        {
            StringBuilder result = new StringBuilder();

            StringBuilder columnProperies = new StringBuilder();
            StringBuilder lockupProperties = new StringBuilder();
            StringBuilder childMethods = new StringBuilder();

            foreach (DataSourceSchema.Column column in tableSchema.Columns.Values)
            {
                columnProperies.AppendFormat("\t\tpublic {0}{2} {1}", column.Type, column.Name, column.AllowNull && !column.Type.Equals("string", StringComparison.InvariantCultureIgnoreCase) ? "?" : String.Empty);
                columnProperies.AppendLine();
                columnProperies.AppendLine("\t\t{");
                if (column.Type.Equals("String", StringComparison.InvariantCultureIgnoreCase))
                    columnProperies.AppendFormat("\t\t\tget {{ return GetColumnString({0}Schema.{1}Column); }}", tableSchema.Name, column.Name);
                else
                    columnProperies.AppendFormat("\t\t\tget {{ return GetColumnValue<{0}>({1}Schema.{2}Column){3}; }}", column.Type, tableSchema.Name, column.Name, !column.AllowNull ? ".GetValueOrDefault()" : String.Empty);

                columnProperies.AppendLine();
                columnProperies.AppendFormat("\t\t\tset {{ SetColumnValue({0}Schema.{1}Column, value); }}", tableSchema.Name, column.Name);
                columnProperies.AppendLine();
                columnProperies.AppendLine("\t\t}");
                columnProperies.AppendLine();

                if (column.ForeyingColumn != null)
                {
                    lockupProperties.AppendFormat("\t\tpublic {0} {1}", DataSourceSchema.Capitalize(column.ForeyingColumn.Table.Name), GenerateLockupName(column));
                    lockupProperties.AppendLine();
                    lockupProperties.AppendLine("\t\t{");
                    lockupProperties.AppendFormat("\t\t\tget {{ return GetLockupValue<{0}>({1}Schema.{2}Column); }}", DataSourceSchema.Capitalize(column.ForeyingColumn.Table.Name), DataSourceSchema.Capitalize( tableSchema.Name), column.Name);
                    lockupProperties.AppendLine();
                    lockupProperties.AppendFormat("\t\t\tset {{ SetLockupValue({0}Schema.{1}Column, value); }}", DataSourceSchema.Capitalize(tableSchema.Name), column.Name);
                    lockupProperties.AppendLine();
                    lockupProperties.AppendLine("\t\t}");
                    lockupProperties.AppendLine();
                }

                foreach (DataSourceSchema.Column child in column.Childs)
                {
                    childMethods.AppendFormat(
        @"
		public DataReaderIterator<{0}> Get{0}List{3}()
		{{
			return {0}.FindByColumn({0}Schema.{1}Column, this.{2});
		}}
        ", DataSourceSchema.Capitalize(child.Table.Name), child.Name, column.Name,
         (column.Childs.Count > 1 ? String.Format("By{0}", child.Name) : String.Empty));
                }

            }

            result.AppendLine(columnProperies.ToString());
            result.AppendLine(lockupProperties.ToString());
            result.AppendLine(childMethods.ToString());

            return result.ToString();
        }

        private static string GenerateSchemaClass(DataSourceSchema.Table tableSchema)
        {
            StringBuilder result = new StringBuilder(SCHEMA_TEMPLATE);

            StringBuilder constants = new StringBuilder();
            StringBuilder columns = new StringBuilder();
            StringBuilder lockups = new StringBuilder();

            foreach (DataSourceSchema.Column column in tableSchema.Columns.Values)
            {
                string columnRealName = column.Name;
                if (column.Name == column.Table.Name)
                    column.Name = String.Format("{0}Value", column.Name);

                constants.AppendFormat("\t\tpublic const string {0}Column = \"{1}\";", column.Name, columnRealName);
                constants.AppendLine();

                if (column.PrimaryKey)
                    columns.AppendFormat("\t\t\tAddColumnInfo(ColumnInfo.CreatePrimaryKeyColumn({0}Column, typeof({1}), {2}));", column.Name, column.Type, (column.Type.ToUpper() == "GUID" || !column.AutoInc) ? "false" : "true");
                else
                    columns.AppendFormat("\t\t\tAddColumnInfo(ColumnInfo.CreateNormalColumn({0}Column, typeof({1}), {3}, null, {2}));", column.Name, column.Type, String.IsNullOrEmpty(column.DefaultValue) ? "null" : column.DefaultValue, column.AllowNull.ToString().ToLower());
                columns.AppendLine();

                if (column.ForeyingColumn != null)
                {
                    lockups.AppendFormat("\t\t\tAddLockupInfo({0}Column, {1}.Schema, LockupType.Joinned, typeof({1}));", column.Name, DataSourceSchema.Capitalize(column.ForeyingColumn.Table.Name));
                    lockups.AppendLine();
                }
            }

            result = result
                .Replace("[TableName]", tableSchema.Name)
                .Replace("[NormalTableName]", tableSchema.Name)
                .Replace("[ColumnNameConstants]", constants.ToString())
                .Replace("[ColumnsConstruction]", columns.ToString())
                .Replace("[LockupsConstruction]", lockups.ToString());


            return result.ToString();
        }

    }
}
